import React from 'react';
import { Form as Form_, Modal as Modal_ } from 'antd';
import { ValidationRule, FormItemProps, FormProps } from 'antd/es/form';
import { WrappedFormUtils } from 'antd/es/form/Form';
import { ModalProps } from 'antd/es/modal';
import { InputProps } from 'antd/es/input';
import { Input } from './input';
import { space } from '../../../tool/reg_exp';
import style from './style.module.less';

interface IFormItemProps extends FormItemProps {
  fill?: boolean; // 占满一行
}

/**
 * 表单 item
 */
export const FormItem: React.SFC<IFormItemProps> = ({ label, fill, className, ...props }) => {
  // 栅格
  const props_ = {
    style: { width: '50%' },
    labelCol: { span: 8 },
    wrapperCol: { span: 14 },
  };

  if (fill) {
    props_.style.width = '100%';
    props_.labelCol.span = 4;
    props_.wrapperCol.span = 19;
  }

  if (label === undefined) {
    props_.labelCol.span = 0;
    props_.wrapperCol.span = 23;
  }

  return <Form_.Item label={label} className={[style.formItem, className].join(' ')} {...props_} {...props} />;
};

interface IValueItemProps {
  name?: string; // 字段名
  rules?: string | ValidationRule[]; // 验证规则
  validator?: (value: any) => string | undefined; // 额外验证器
  initialValue?: string | number | boolean; // 默认值
  label?: string; // 标题
  fill?: boolean; // 占满一行
  itemProps?: FormItemProps;
  inputProps?: InputProps;
  children?: JSX.Element | JSX.Element[];
}

/**
 * 创建字段组件
 */
export const createValueItem = (
  getFieldDecorator: WrappedFormUtils['getFieldDecorator']
): React.SFC<IValueItemProps> => ({
  name,
  rules = [],
  validator,
  initialValue,
  children,
  label,
  fill,
  itemProps,
  inputProps,
}) => {
  // 自动加入必填提示
  if (typeof rules === 'string') {
    rules = [{ required: true, message: rules }];
  }

  // 不可提交空格
  rules.push({
    validator: (_rule, value, callback) => {
      let msg;
      if (value && typeof value === 'string' && !space.test(value)) msg = '不可提交空格';
      if (!msg && validator) msg = validator(value);
      callback(msg);
    },
  });

  return (
    <FormItem label={label} fill={fill} {...itemProps}>
      {name
        ? getFieldDecorator(name, {
            rules,
            initialValue,
            validateFirst: true,
          })(children ? children[0] || children : <Input {...inputProps} />)
        : children}
    </FormItem>
  );
};

interface IFormBoxProps {
  onSubmit: (err: any, value: any) => Promise<void>; // 提交表单回调
  children: (ValueItem: React.SFC<IValueItemProps>) => JSX.Element; // 表单值创建组件
  onForm?: (form: WrappedFormUtils) => void; // 获取表单数据对象
  onRef?: (formBox: FormBox_) => void; // 获取表单组件
  form: WrappedFormUtils;
  formProps?: FormProps; // 表单 props
}

class FormBox_ extends React.Component<IFormBoxProps> {
  ValueItem = createValueItem(this.props.form.getFieldDecorator);

  componentDidMount() {
    const { onForm, form, onRef } = this.props;
    onForm && onForm(form);
    onRef && onRef(this);
  }

  submit = (e?: React.FormEvent) => {
    e && e.preventDefault();
    const { form, onSubmit } = this.props;
    form.validateFieldsAndScroll(onSubmit);
  };

  render() {
    const { children, formProps = {} } = this.props;
    const { className, ...formProps_ } = formProps;
    return (
      <Form_ onSubmit={this.submit} className={[style.form, className].join(' ')} {...formProps_}>
        {children(this.ValueItem)}
      </Form_>
    );
  }
}

/**
 * 表单模块
 */
export const FormBox = Form_.create()(FormBox_);

interface IModalProps {
  title: string; // 弹窗标题
  showButton: JSX.Element; // 弹出按钮
  onShow?: () => void; // 显示回调，常用于表单值初始化，请使用异步调用 onForm 返回的 form，确保表单数据对象已获取
  onHide?: () => void; // 隐藏回调
  modalProps?: ModalProps; // 弹窗 props
  onSubmit: (err: any, value: any) => Promise<boolean>; // 提交表单回调，返回 true 则隐藏弹窗
  children: IFormBoxProps['children'];
  onForm?: IFormBoxProps['onForm']; // 弹窗弹出，表单渲染后才会执行该函数
  formProps?: FormProps; // 表单 props
}

interface IModalState {
  visible: boolean;
  loading: boolean;
}

/**
 * 弹出层表单
 */
export class ModalFormBox extends React.Component<IModalProps, IModalState> {
  state = { visible: false, loading: false };

  formBox?: any;

  onShow = () => this.setState({ visible: true }, this.props.onShow);
  onHide = () => this.setState({ visible: false }, this.props.onHide);
  onOk = () => this.formBox.submit();

  onSubmit = async (err: any, value: any) => {
    const state: any = { loading: true };
    this.setState(state);
    state.visible = !(await this.props.onSubmit(err, value));
    state.loading = false;
    this.setState(state);
  };

  render() {
    const { title, showButton, modalProps, children, onForm, formProps } = this.props;
    const { visible, loading } = this.state;
    return (
      <>
        {React.cloneElement(showButton, { onClick: this.onShow })}
        <Modal_
          title={title}
          onCancel={this.onHide}
          onOk={this.onOk}
          visible={visible}
          confirmLoading={loading}
          destroyOnClose
          {...modalProps}
        >
          <FormBox
            onSubmit={this.onSubmit}
            onRef={ref => (this.formBox = ref)}
            children={children}
            onForm={onForm}
            formProps={{
              layout: 'inline',
              className: style.modalFormBox,
              ...formProps,
            }}
          />
        </Modal_>
      </>
    );
  }
}

interface IFormBoxSearchProps {
  onSubmit: IFormBoxProps['onSubmit'];
  children: IFormBoxProps['children'];
  onForm?: IFormBoxProps['onForm'];
  formProps?: FormProps; // 表单 props
}

/**
 * 搜索表单布局
 */
export class FormBoxSearch extends React.Component<IFormBoxSearchProps> {
  formBox?: any;

  componentDidMount() {
    const ValueItem = this.formBox.ValueItem;
    this.formBox.ValueItem = (props: IValueItemProps) => {
      const { itemProps, ...props_ } = props;
      return (
        <ValueItem
          itemProps={{
            style: {},
            labelCol: {},
            wrapperCol: {},
            ...itemProps,
          }}
          {...props_}
        />
      );
    };
  }

  render() {
    const { formProps, ...props } = this.props;
    return (
      <FormBox
        onRef={ref => (this.formBox = ref)}
        formProps={{
          layout: 'inline',
          ...formProps,
        }}
        {...props}
      />
    );
  }
}
